证书续签的挑战
Let's Encrypt 证书有效期仅 90 天,acme.sh 会在到期前 30 天自动续签。但续签后新证书替换旧证书时,需要重启或重载使用证书的服务(Nginx、Node.js 应用等),否则它们继续使用内存中的旧证书。
滚动更新策略
滚动更新的核心思路是:不中断现有服务的情况下完成证书替换。
方案一:Nginx 重载(推荐)
Nginx 支持 reload 操作,会平滑地用新配置(包括新证书)启动新的 Worker 进程,同时让旧 Worker 处理完当前请求后退出。整个过程对用户透明。
acme.sh --install-cert -d example.com \
--key-file /etc/nginx/ssl/example.com/key.pem \
--fullchain-file /etc/nginx/ssl/example.com/fullchain.pem \
--reloadcmd "systemctl reload nginx"
bash
方案二:Node.js 应用的证书热更新
Node.js 应用启动时将证书读入内存,证书更新后需要重新读取。可以通过文件监听或定时检查实现:
import * as fs from 'fs'
import * as path from 'path'
let httpsOptions = {
key: fs.readFileSync(path.resolve('certs/key.pem')),
cert: fs.readFileSync(path.resolve('certs/cert.pem')),
}
// 定时检查证书文件是否更新
setInterval(() => {
try {
const newKey = fs.readFileSync(path.resolve('certs/key.pem'))
const newCert = fs.readFileSync(path.resolve('certs/cert.pem'))
if (newKey.toString() !== httpsOptions.key.toString() ||
newCert.toString() !== httpsOptions.cert.toString()) {
httpsOptions.key = newKey
httpsOptions.cert = newCert
console.log('SSL证书已热更新')
}
} catch (error) {
console.error('证书更新检查失败:', error)
}
}, 3600000) // 每小时检查一次
typescript
但 Node.js 原生的 HTTPS 服务器不支持真正的证书热更新——已经建立的 TLS 连接使用的是旧证书。要完全生效,仍然需要重启 Node.js 进程。
方案三:PM2 零停机重启
# 使用 PM2 管理的 Node.js 应用
pm2 reload app --update-env
bash
PM2 的 reload 会先启动新的实例,等它就绪后再关闭旧实例,实现零停机。
完整的自动化续签流程
acme.sh cron 定时检查
|
v
证书即将过期? --否--> 等待下次检查
|
是
v
执行续签(HTTP/DNS 验证)
|
v
续签成功?
| |
是 否 --> 记录错误日志,发送告警
|
v
安装新证书到目标目录
|
v
执行 --reloadcmd
|
v
验证 HTTPS/WSS 是否正常
|
v
记录续签日志
text
证书更新后的验证脚本
#!/bin/bash
# verify-ssl.sh
DOMAIN="example.com"
EXPECTED_DAYS=90
# 检查证书有效期
EXPIRY_DATE=$(echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN":443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
echo "证书剩余有效期: $DAYS_LEFT 天"
if [ "$DAYS_LEFT" -lt 7 ]; then
echo "警告: 证书即将过期!"
# 发送告警通知(邮件/钉钉/企业微信)
fi
bash
多服务器场景
如果有多台服务器使用同一域名(负载均衡),证书更新需要在每台服务器上执行。可以:
- 在一台主服务器上续签证书,通过脚本将证书同步到其他服务器
- 使用共享存储(如 NFS、对象存储)存放证书
- 使用 ACME 的 DNS 验证 + 自动化 CI/CD 管道统一管理证书
告警通知
证书续签失败或即将过期时,需要及时通知运维人员。常见通知方式:
- 邮件通知
- 钉钉/企业微信 Webhook
- Slack 通知
- 监控系统集成(Prometheus AlertManager、Zabbix 等)
↑